﻿/*
 * This file is part of quickReminder, a software for gamers
 *
 * Copyright (C) 2006-2009 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading;

using HtmlAgilityPack;
using LucasCode.PublicTypes;
using IoHandler;

namespace ESLFileArchiver
{
    public class WebSynchronizer
    {
        public static bool isSomeMatchInReach(TimeSpan before, TimeSpan after, string cookie, StringCollection MatchFilter)
        {
            Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;

            MatchArchive[] matches = WebIO.getOpenMatches(cookie);
            foreach (MatchArchive match in matches)
            {
                if (IsDateBetween(match.MatchDate, DateTime.Now, before, after))
                {
                    System.Diagnostics.Trace.WriteLine(String.Format("{0} is between {1}min before and {2}min after the match",match.MatchDate.ToString(),before.ToString(),after.ToString()));

                    if (MatchFilter != null)
                        foreach (string filter in MatchFilter)
                        {
                            System.Diagnostics.Trace.WriteLine("Found matchfilter: " + filter + " and tries to match " + match.Matchlink);

                            if (Regex.Match(match.Matchlink, filter, RegexOptions.Singleline | RegexOptions.IgnoreCase).Success)
                            {
                                System.Diagnostics.Trace.WriteLine(String.Format("Match vs. '{0}' on {1} matches filter '{2}'!", match.Enemy.Name, match.MatchDate, filter));
                                return true;
                            }
                        }
                }
            }
            return false;

        }

        private static bool IsDateBetween(DateTime target, DateTime a, TimeSpan before, TimeSpan after)
        {
            return (a >= target - before && a <= target + after);
        }

        private static MatchPlayer mergePlayer(MatchPlayer toBeFilled, MatchPlayer toBeDeleted)
        {
            toBeFilled.AddActions(toBeDeleted.Actions);

            foreach (GameAccount acc in toBeDeleted.GameAccounts)
                toBeFilled.AddGameAccount(acc);

            toBeFilled.IsArchiveOwner |= toBeDeleted.IsArchiveOwner;
            toBeFilled.HasAequitasLog |= toBeDeleted.HasAequitasLog;

            if (!string.IsNullOrEmpty(toBeDeleted.GameID) && string.IsNullOrEmpty(toBeFilled.GameID))
                toBeFilled.GameID = toBeDeleted.GameID;

            if (!string.IsNullOrEmpty(toBeDeleted.ESLNick) && string.IsNullOrEmpty(toBeFilled.ESLNick))
                toBeFilled.ESLNick = toBeDeleted.ESLNick;

            if (!string.IsNullOrEmpty(toBeDeleted.UserID) && string.IsNullOrEmpty(toBeFilled.UserID))
                toBeFilled.UserID = toBeDeleted.UserID;

            if (toBeFilled.Team == null && toBeDeleted.Team != null)
                toBeFilled.Team = toBeDeleted.Team;

            if (toBeDeleted.CurrentNick != toBeFilled.CurrentNick)
            {
                toBeFilled.AddNick(toBeDeleted.CurrentNick);
                toBeFilled.AddNicks(toBeDeleted.Names);
                if (toBeFilled.ESLNick != toBeDeleted.CurrentNick)
                    toBeFilled.CurrentNick = toBeDeleted.CurrentNick;
            }

            System.Diagnostics.Trace.WriteLine("Successfully merged " + toBeFilled.CurrentNick + " and " + toBeDeleted.CurrentNick);

            return toBeFilled;
        }

        private static MatchArchive removeTeamkills(MatchArchive archive)
        {
            var count = 0;

            System.Diagnostics.Trace.WriteLine("Start removing Teamkills at " + DateTime.Now);
            var todelete = new List<MatchAction>();

            foreach (MatchPlayer player in archive.Players)
            {
                foreach (MatchAction action in player.Actions)
                {
                    if ((archive.getPlayerwithqRID(action.ObjectID) != null) && (archive.getPlayerwithqRID(action.SubjectID) != null))
                        if ((archive.getPlayerwithqRID(action.ObjectID).Team != null) && (archive.getPlayerwithqRID(action.SubjectID).Team != null))
                            if (archive.getPlayerwithqRID(action.ObjectID).Team == archive.getPlayerwithqRID(action.SubjectID).Team)
                                todelete.Add(action);
                }

                foreach (MatchAction action in todelete)
                {
                    player.RemoveAction(action);
                    count++;
                }
            }

            System.Diagnostics.Trace.WriteLine("Finished removing " + (count/2) + " Teamkills at " + DateTime.Now);

            return archive;
        }

        public static void MergePlayers(MatchArchive matchArchive)
        {
            System.Diagnostics.Trace.WriteLine("WebSynchronizer MergePlayers for match " + matchArchive.Matchlink + " started.");

            //double i = 0;
            //UpdateProgress(0);
            int max = matchArchive.TeamlessPlayers.GetLength(0);
            if (string.IsNullOrEmpty(matchArchive.Matchlink))
            {
                MatchPlayer[] players = matchArchive.TeamlessPlayers;
                foreach (MatchPlayer p in players)
                {
                    WebIO.updatePlayerInformationFromWebsite(p);
                }
            }
            else
            {
                var removePlayerFromTeamless = new List<MatchPlayer>();
                var removePlayerFromTeam = new List<MatchPlayer>();
                var addToContestant = new List<MatchPlayer>();

                var temp = new MatchPlayer();

                bool foundPlayer;
                
                foreach (MatchPlayer tp in matchArchive.TeamlessPlayers)
                {
                    foundPlayer = false;

                    //TODO: cleanup (stil needed?)
                    /*if (tp.CurrentNick.StartsWith("with headshot"))
                    {
                        removePlayerFromTeamless.Add(tp);
                        break;
                    }*/

                    foreach (MatchContestant contestant in matchArchive.Contestants)
                    {
                        foreach (MatchPlayer p in contestant.Players)
                        {
                            if (p.HasGameAccountWithID(tp.GameID))
                            {
                                if (p.QrID < tp.QrID)
                                {
                                    mergePlayer(p, tp);

                                    foundPlayer = true;
                                    removePlayerFromTeamless.Add(tp);
                                    break;
                                }
                                else
                                {
                                    temp = mergePlayer(tp, p);

                                    addToContestant.Add(temp);
                                    removePlayerFromTeamless.Add(tp);

                                    foundPlayer = true;
                                    removePlayerFromTeam.Add(p);
                                    break;
                                }
                            }

                            //?? können 2 Spieler gemerged werden, wenn sie nur dne selben Nick haben?
                            if (p.CurrentNick == tp.CurrentNick)
                            {
                                if (p.QrID < tp.QrID)
                                {
                                    mergePlayer(p, tp);

                                    if (matchArchive.Owner == tp)
                                        matchArchive.Owner = p;

                                    removePlayerFromTeamless.Add(tp);
                                    foundPlayer = true;
                                    break;
                                }
                                else
                                {
                                    temp = mergePlayer(tp, p);

                                    if (matchArchive.Owner == p)
                                        matchArchive.Owner = temp;

                                    addToContestant.Add(temp);

                                    removePlayerFromTeamless.Add(temp);
                                    removePlayerFromTeam.Add(p);
                                    foundPlayer = true;
                                    break;
                                }
                            }
                        }

                        foreach (MatchPlayer player in removePlayerFromTeam)
                            contestant.removePlayer(player);

                        foreach (MatchPlayer player in addToContestant)
                            contestant.addPlayer(player);

                        addToContestant.Clear();
                        removePlayerFromTeam.Clear();

                        if (foundPlayer)
                            break;
                    }
                }

                foreach (MatchPlayer p in removePlayerFromTeamless)
                    matchArchive.RemoveTeamlessPlayer(p);

                removePlayerFromTeamless.Clear();
                
                //?? weiß nicht ob diese Strategie gut ist
                //ich schaue als letztes ob einer der TeamlessPlayer einen ESL Nick enthält um den Matchplayer
                //dann zu mergen
                foreach (MatchPlayer tp in matchArchive.TeamlessPlayers)
                {
                    foundPlayer = false;

                    foreach (MatchContestant contestant in matchArchive.Contestants)
                    {
                        foreach (MatchPlayer p in contestant.Players)
                        {
                            if (!string.IsNullOrEmpty(p.ESLNick))
                                if (tp.CurrentNick.Contains(p.ESLNick))
                                {
                                    if (p.QrID < tp.QrID)
                                    {
                                        mergePlayer(p, tp);

                                        if (matchArchive.Owner == tp)
                                            matchArchive.Owner = p;

                                        removePlayerFromTeamless.Add(tp);
                                        foundPlayer = true;
                                        break;
                                    }
                                    else
                                    {
                                        temp = mergePlayer(tp, p);

                                        if (matchArchive.Owner == p)
                                            matchArchive.Owner = temp;

                                        addToContestant.Add(temp);

                                        removePlayerFromTeamless.Add(temp);
                                        removePlayerFromTeam.Add(p);
                                        foundPlayer = true;
                                        break;
                                    }
                                }
                        }

                        foreach (MatchPlayer player in removePlayerFromTeam)
                            contestant.removePlayer(player);

                        foreach (MatchPlayer player in addToContestant)
                            contestant.addPlayer(player);

                        addToContestant.Clear();
                        removePlayerFromTeam.Clear();

                        if (foundPlayer)
                            break;
                    }

                    if (!foundPlayer)
                        System.Diagnostics.Trace.WriteLine("Couldn't match player with nick " + tp.CurrentNick);
                    else removePlayerFromTeamless.Add(tp);
                }

                foreach (MatchPlayer p in removePlayerFromTeamless)
                    matchArchive.RemoveTeamlessPlayer(p);

                matchArchive = removeTeamkills(matchArchive);
            }

            System.Diagnostics.Trace.WriteLine("WebSynchronizer MergePlayers for match " + matchArchive.Matchlink + " successfully finished");
        }

        public static MatchArchive UpdateDetailsFromWeb(string cookie, MatchInfoFlags flags, MatchArchive matchArchive)
        {
            System.Diagnostics.Trace.WriteLine("WebSynchronizer UpdateDetailsFromWeb for match " + matchArchive.Matchlink);
            DateTime start = DateTime.Now;

            if (string.IsNullOrEmpty(matchArchive.Matchlink) || flags == MatchInfoFlags.None)
                return null;
            //UpdateProgress(10);
            string HTML = WebIO.GetHTML(matchArchive.Matchlink, cookie);
            //UpdateProgress(50);
            
            var d = new HtmlDocument();
            d.LoadHtml(HTML);

            System.Diagnostics.Trace.WriteLine("START UpdateDetailsFromWeb HTML DUMP--------------------------------------------");
            System.Diagnostics.Trace.WriteLine("///////////////////////////////////////////////////////////");
            System.Diagnostics.Trace.WriteLine(HTML);
            System.Diagnostics.Trace.WriteLine("///////////////////////////////////////////////////////////");
            System.Diagnostics.Trace.WriteLine("END UpdateDetailsFromWeb HTML DUMP----------------------------------------------");
            
            
            HtmlNode matchinfos = d.DocumentNode.SelectSingleNode(@"//div[@id=""main_content""]/table");
            

            //alle parameter des matches:
            // //div[@id='main_content']//td[@colspan='2' and text()='Parameters']/../following-sibling::tr[1]/td/table/tr/td[text()='MatchID']/../../tr


            HtmlNodeCollection Points = matchinfos.SelectNodes("tr[5]/td/table/tr[position()<3]/td/b");
            HtmlNodeCollection Maps = matchinfos.SelectNodes("//td[@colspan='2' and text()='Parameters']/../following-sibling::tr[1]/td/table/tr/td[text()='map']/following-sibling::td | //td[@class='WindowHeader' and text()='Result']/../following-sibling::tr[1]/td/table[@cellpadding='2']/tr[position()<=2]/td[1]");

            HtmlNodeCollection Contestants = matchinfos.SelectNodes("tr[3]/td/table/tr[2]/td/a[1]");

            HtmlNodeCollection Aequitas = matchinfos.SelectNodes(@"//tr[@id=""aquetias_public_logs""]/td/table/tr");

            HtmlNodeCollection MatchDateInfo = matchinfos.SelectNodes(@"//div[@id=""main_content""]/table/tr[3]/td[2]/table/tr[3]/td[2]");

            HtmlNodeCollection Lineup;

            if (matchinfos.SelectNodes("tr[6]/td")[0].InnerText == "Matchmedia Requests")
            {
                if (matchinfos.SelectNodes("tr[8]/td")[0].InnerText == "Lineup")
                    Lineup = matchinfos.SelectNodes("tr[9]/td/table/tr[2]");
            }
            else if (matchinfos.SelectNodes("tr[6]/td")[0].InnerText == "Lineup")
                Lineup = matchinfos.SelectNodes("tr[7]/td/table/tr[2]");


            HtmlNode ladder = d.DocumentNode.SelectSingleNode(@"//div[@id='navi_breadcrumb']/div/b/a");
            if (ladder != null)
                matchArchive.Ladder = ladder.InnerText.Trim();

            //UpdateProgress(60);

            string datestr = MatchDateInfo[0].InnerText.Substring(MatchDateInfo[0].InnerText.IndexOf(',') + 1, MatchDateInfo[0].InnerText.Length - MatchDateInfo[0].InnerText.IndexOf(',') - 1);

            datestr = datestr.Replace('/', '.');

            matchArchive.MatchDate = DateTime.Parse(datestr, CultureInfo.GetCultureInfo("de-DE"));

            if ((flags & MatchInfoFlags.ScoreInfo) > 0)
            {            
                for (int i = 0; i < Points.Count / 2; i++)
                {
                    var r = new MatchRound();
                    if (Maps != null)
                    {
                        r.Map = Maps[i].InnerText;
                    }

                    try
                    {
                        r.Scores[0] = int.Parse(Points[i * 2].InnerText);
                        r.Scores[1] = int.Parse(Points[(i * 2) + 1].InnerText);
                    }
                    catch 
                    {
                        System.Diagnostics.Trace.WriteLine("No result entered yet, can't fill score");
                    }

                    matchArchive.AddRound(r);
                }
            }
            


            /* this is what a teamid list in the ESL source looks like
            ESLteamID.unshift(2470639);
            ESLteamID.unshift(1645201);
            ESLteamID.unshift(2804185);
            ESLteamID.unshift(2658316);
            ESLteamID.unshift(2466101);
            ESLteamID.unshift(2466119);
            ESLteamID.unshift(2659890);
            ESLteamID.unshift(2489288);
            ESLteamID.unshift(1642763); */
            

            

            if ((flags & MatchInfoFlags.TeamInfo) > 0)
            {
                matchArchive.OwnTeam = null;
                MatchCollection MyTeamIDs = Regex.Matches(HTML, @"\bESLteamID.unshift\((\d+)\);");
                var TeamIDs = new StringCollection();

                MatchContestant team;

                foreach (Match m in MyTeamIDs)
                {
                    TeamIDs.Add(m.Groups[1].Value);
                }
                foreach (HtmlNode contestant in Contestants)
                {
                    if (matchArchive.containsContestant(WebIO.getIDFromLink(contestant.Attributes["href"].Value)))
                    {
                        team = matchArchive.getContestant(WebIO.getIDFromLink(contestant.Attributes["href"].Value));
                    }
                    else
                    {
                        team = new MatchContestant();

                        team.ID = WebIO.getIDFromLink(contestant.Attributes["href"].Value);
                        team.Logo = WebIO.GetBitmapFromURL(contestant.SelectSingleNode("img").Attributes["src"].Value);
                        team.Name = contestant.InnerText.Trim();

                        matchArchive.AddContestant(team);
                    }

                    if (contestant.Attributes["href"].Value.Contains("/player/"))
                    {
                        team.Type = MatchContestantTypes.Individual;
                        var p = new MatchPlayer();

                        p.CurrentNick = team.Name;
                        p.ESLNick = team.Name;
                        p.UserID = team.ID;

                        team.addPlayer(p);
                    }

                    if (TeamIDs.Contains(team.ID))
                        matchArchive.OwnTeam = team;
                }
            }

            if ((flags & MatchInfoFlags.PlayerInfo) > 0)
            {
                matchArchive.Owner = null;
                string MyUserID = string.Empty;
                Match MyUserIDRegex = Regex.Match(HTML, @"ESLuserID = (\d+)");

                MatchPlayer player;

                if (MyUserIDRegex.Success)
                    MyUserID = MyUserIDRegex.Groups[1].Value;
                if (Aequitas != null)
                    foreach (HtmlNode n in Aequitas)
                    {
                        if (matchArchive.containsPlayer(WebIO.getIDFromLink(n.SelectSingleNode("td[1]/a").Attributes["href"].Value)))
                        {
                            player =
                                matchArchive.getPlayer(
                                    WebIO.getIDFromLink(n.SelectSingleNode("td[1]/a").Attributes["href"].Value));
                            
                            if (string.IsNullOrEmpty(player.ESLNick))
                                player.ESLNick = n.SelectSingleNode("td[1]/a").InnerText;

                            if (string.IsNullOrEmpty(player.CurrentNick))
                                player.CurrentNick = player.ESLNick;

                            player.HasAequitasLog = true;
                        }
                        else
                        {
                            player = new MatchPlayer();

                            player.UserID = WebIO.getIDFromLink(n.SelectSingleNode("td[1]/a").Attributes["href"].Value);
                            player.ESLNick = n.SelectSingleNode("td[1]/a").InnerText;
                            player.CurrentNick = player.ESLNick;
                            player.HasAequitasLog = true;

                            player.GameAccounts = WebIO.getGameAccountsForID(player.UserID);

                            if (player.UserID == MyUserID)
                                matchArchive.Owner = player;
                        }

                        matchArchive.AddUserwithAeq(player.UserID);

                        string teamid = WebIO.getIDFromLink(n.SelectSingleNode("td[2]/a").Attributes["href"].Value);

                        bool foundteam = false;
                        
                        
                        foreach (MatchContestant contestant in matchArchive.Contestants)
                        {
                            if (foundteam)
                                break;
                            switch (contestant.Type)
                            {
                                case MatchContestantTypes.Individual:
                                    if (contestant.ID == player.UserID)
                                    {
                                        foundteam = true;
                                        contestant.Players[0].HasAequitasLog = true;
                                        //c.addPlayer(player);
                                        break;
                                    }
                                    break;
                                case MatchContestantTypes.Team:
                                    if (contestant.ID == teamid)
                                    {
                                        foundteam = true;
                                        if (!contestant.containsPlayer(player))
                                            contestant.addPlayer(player);
                                        break;
                                    }
                                    break;
                            }
                        }

                        if (!foundteam)
                        {
                            bool dupfound = false;
                            foreach (MatchContestant contestant in matchArchive.Contestants)
                            {
                                if (contestant.searchDuplicate(player) != null)
                                {
                                    contestant.addPlayer(player);
                                    dupfound = true;
                                    break;
                                }
                            }
                            if (!dupfound)
                            {
                                foreach (MatchPlayer p in matchArchive.TeamlessPlayers)
                                    if (p.CompareTo(player) == 0)
                                        dupfound = true;
                                if (!dupfound)
                                    matchArchive.AddTeamlessPlayer(player);

                            }
                            
                        }
                    }
            }

            System.Diagnostics.Trace.WriteLine("WebSynchronizer UpdateDetailsFromWeb successfully proceeded for match: " + matchArchive.Matchlink + ". Took " + DateTime.Now.Subtract(start));

            return matchArchive;
        }
    }
}
